#!/usr/bin/env python

import pygtk
pygtk.require('2.0')
import sys
import gtk
import gobject
import gnomeapplet
import socket
import re
try:
    import pynotify
except ImportError:
    have_notify=False
else:
    have_notify=True


from collections import deque
from time import sleep
from mlcommander import mlcommander
from os.path import realpath, dirname

html_codes = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    '\'': '&#39;',
}


class monitor_chart(gtk.DrawingArea):
    def __init__(self, width=100, height=20):
        gtk.DrawingArea.__init__(self)
        self.connect("expose_event", self.expose_cb)
        self.width, self.height = width, height
        self.set_size_request(width, height)

        self.father = None
        self.rate = 0
        self.chart_buf = deque([0 for i in range(0, self.width)])
        self.status_text = ''
        self.stroke_style = (1, 0, 0)
        self.max_height_coeff = 0.8
        
    def update_data(self, rate):
        self.rate = rate
        
        if self.get_parent() == None:
            return True
        width, height = self.get_parent().size_request()
        #Adds region to the update area for window
        self.window.invalidate_rect((0, 0, width, height), False)
        return True
    
        
    def expose_cb(self, widget, event):
        self.context = widget.window.cairo_create()
        # set a clip region for the expose event
        self.context.rectangle(event.area.x, event.area.y,
                               event.area.width, event.area.height)
        self.context.clip()
        self.repaint(self.context)
        return False

    def repaint(self, context):
        if self.rate > self.father.recent_max_rate:
            self.father.recent_max_rate = self.rate
        v = self.rate / self.father.recent_max_rate * self.max_height_coeff * self.height
        #print self.rate, v

        context.set_source_rgb(0, 0, 0)
        context.rectangle(0, 0, self.width, self.height);    
        context.fill()

        self.chart_buf.append(v)
        self.chart_buf.popleft()

        r, g, b = self.stroke_style
        context.set_source_rgb(r, g, b);
        for i  in range(0, self.width):
            context.move_to(i, self.height);
            context.line_to(i, self.height-self.chart_buf[i]);
        context.stroke();


class mlmonitor :
    def __init__ (self):
        self.dchart = monitor_chart(28, 20)
        self.uchart = monitor_chart(28, 20)
        self.dchart.stroke_style = (0, 1, 0)
        self.uchart.stroke_style = (1, 0, 0)
        self.dchart.father = self.uchart.father = self

        self.commander = mlcommander()
        self.applet = None

        self.regspd = re.compile('\[.*\].*?mldonkey (.*?)\n')
        self.reginfo = re.compile('Down:(.*?)KB.*?Up:(.*?)KB')
        self.recent_max_rate = 0.0001

        self.notify_title = 'Not Connected Yet'
        self.notify_body = 'No data'
        self.notify_title_template =  'Down: %sKB/s; Up: %sKB/s'
        self.notify_body_template =  '<span>%s</span>\n - <span color="blue">(%s%%: %s/%s: %sKb/s)</span>' 
        self.tooltips_body_template = '%s - (%s%%: %s/%s" %sKb/s)'
        
        if have_notify:
            pynotify.init('MLMonistor_Applet')

        pass
        
    def clicked_cb(self, widget, events):
    	if have_notify:
    	    n = pynotify.Notification(self.notify_title, self.notify_body, '/usr/share/pixmaps/mlnet_logo.png')
    	    n.set_timeout(5000)
    	    #n.add_action('clicked', ' Close ', lambda :pass)
    	    n.attach_to_widget(self.applet)
    	    n.show()
    	else:
    	    pass
    		#TODO: How to display messages while no pynotify?

    def parse_info(self, str):
        out = []
        oinfo = self.regspd.findall(str)
        if len(oinfo) != 0:
            for e in oinfo:
                out.append(filter(lambda x: len(x)!=0 , e.split(' ')))
        #name, perc, cs, ts, sn, old, acts, rate, prio
        return out

    def parse_rate(self, str):
        d, u = [float(e.strip()) for e in self.reginfo.search(str).groups()]
        return (d, u)
        
    def update_applet(self):
        if not self.commander.is_connected:
            return True
        lines = []
        str = self.commander.execute('vd');
        if str.find('Down') == -1:
            return True

        drate, urate = self.parse_rate(str)
        self.notify_title = self.notify_title_template % (drate, urate)

        info = self.parse_info(str)
        for name, perc, cs, ts, sn, old, acts, rate, prio in info:
            for key, code in html_codes.iteritems():
                name = name.replace(key, code)
            lines.append((name, perc, cs, ts, rate))
            
        self.notify_body = '\n'.join([ self.notify_body_template % x for x in lines])
        
        self.applet.set_tooltip_text(self.notify_title +'\n' + '\n'.join([self.tooltips_body_template % x for x in lines]))

        self.dchart.update_data(drate)
        self.uchart.update_data(urate)
        return True

    def schedule_timeout(self):
        gobject.timeout_add(1000, self.update_applet)


    def sample_factory(self, applet, iid):
        if iid != 'OAFIID:GNOME_MLMonitor':
            return False
        print "Creating new applet instance"
        self.applet = applet
        
        box = gtk.HBox()
        box.add(self.dchart)
        box.add(self.uchart)
        box.child_set_property(self.dchart, 'expand', False)
        box.child_set_property(self.uchart, 'expand', False)
        box.child_set_property(self.dchart, 'fill', False)
        box.child_set_property(self.uchart, 'fill', False)
        box.set_spacing(1)

        ebox = gtk.EventBox()
        ebox.connect("button-press-event", self.clicked_cb)
        ebox.set_border_width(1)
        ebox.add(box)
        w, h = ebox.get_size_request()
        self.applet.set_size_request(w, h)
        self.applet.add(ebox)
        self.applet.show_all()
        self.schedule_timeout();
        return True

if len(sys.argv) == 2 and sys.argv[1] == "w":   
    main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
    main_window.set_title("Python Applet")
    main_window.connect("destroy", gtk.mainquit) 
    app = gnomeapplet.Applet()
    mlmonitor().sample_factory(app, 'OAFIID:GNOME_MLMonitor')
    app.reparent(main_window)
    main_window.show_all()
    gtk.main()
    
else:
    print "Starting factory"
    try:
        gnomeapplet.bonobo_factory("OAFIID:GNOME_MLMonitor_Factory", 
                       gnomeapplet.Applet.__gtype__, 
                       "MLMonitor", "0", mlmonitor().sample_factory)
    except:
        sys.exit()

    print "Factory ended"

